from collections import defaultdict
from datetime import datetime

from flask import request, jsonify
from flask_restx import Namespace, Resource, fields
from flask_jwt_extended import jwt_required
from sqlalchemy import and_

# 导入  模型
from app1.models import Zbfwb_machine_cmdb
from app1 import db
from app1 import api

from app1.customdate import CustomDate
from app1.utils import format_response, format_response_page

# 创建 Flask-RESTx 命名空间
ns_machine_cmdb = Namespace('ns_machine_cmdb', description='machine_cmdb operations')

# 使用 Flask-RESTx
# 使用 Flask-RESTx 定义模型
machine_cmdb_model = ns_machine_cmdb.model(
    'machine_cmdb',
    {
        "source": fields.String(description='来源'),
        "machine_type": fields.String(description='服务器类型'),
        "service_catalog": fields.String(description='服务目录编号'),
        "app_name": fields.String(description='系统名'),
        "project_name": fields.String(description='项目名'),
        "app_abbre": fields.String(description='系统简称'),
        "availability_level": fields.String(description='可用性等级'),
        "dr_level": fields.String(description='灾备等级'),
        "machine_name": fields.String(description='主机名'),
        "ip": fields.String(description='IP'),
        "dmz_ip": fields.String(description='DMZ内网IP'),
        "data_center": fields.String(description='数据中心'),
        "network_area": fields.String(description='网络区域'),
        "resource_pool": fields.String(description='资源池'),
        "host_machine": fields.String(description='宿主机信息'),
        "physical_machine": fields.String(description='物理机序列号'),
        "server_type": fields.String(description='服务器角色'),
        "os_level": fields.String(description='操作系统版本'),
        "middleware": fields.String(description='中间件'),
        "status": fields.String(description='状态'),
        "online_time": fields.String(description='上线时间'),
        "run_status": fields.String(description='运行状态'),
        "rec_date": fields.String(description='录入时间'),
        "role_a": fields.String(description='A角'),
        "role_b": fields.String(description='B角'),
        "role_c": fields.String(description='C角')
    }
)

# 定义外层的响应模型
response_model = ns_machine_cmdb.model('Response_detail', {
    'message': fields.Nested(ns_machine_cmdb.model('Message_count_detail', {
        "result": fields.Integer(description='查询结果返回值'),
        "result_text": fields.String(description='查询结果信息'),
        'count': fields.Integer(description='查询结果返回数据条数'),
        'data': fields.List(fields.Nested(machine_cmdb_model))
    }))
})

# fields.Raw 格式说明
# {"source": [
#       {
#         "value": "GCMP",
#         "count": 200
#       },
#       {
#          "value": "Atlas",
#          "count": 100
#        }
#       ]
# }
# 定义外层的响应模型
response_count_model = ns_machine_cmdb.model('Response_Count_detail', {
    'message': fields.Nested(ns_machine_cmdb.model('Message_count_detail', {
        "result": fields.Integer(description='查询结果返回值'),
        "result_text": fields.String(description='查询结果信息'),
        'count': fields.Integer(description='查询结果返回数据条数'),
        'data': fields.List(fields.List(fields.Raw))
    }))
})

machine_cmdb_query_model = ns_machine_cmdb.model(
    'machine_cmdb_query',
    {
        "source": fields.List(fields.String, description='来源', default=[]),
        "machine_type": fields.List(fields.String, description='服务器类型', default=[]),
        "service_catalog": fields.List(fields.String, description='服务目录编号', default=[]),
        "app_name": fields.List(fields.String, description='系统名', default=[]),
        "project_name": fields.List(fields.String, description='系统名', default=[]),
        "app_abbre": fields.List(fields.String, description='系统简称', default=[]),
        "availability_level": fields.List(fields.String, description='可用性等级', default=[]),
        "dr_level": fields.List(fields.String, description='灾备等级', default=[]),
        "machine_name": fields.List(fields.String, description='主机名', default=[]),
        "ip": fields.List(fields.String, description='IP', default=[]),
        "dmz_ip": fields.List(fields.String, description='DMZ内网IP', default=[]),
        "data_center": fields.List(fields.String, description='数据中心', default=[]),
        "network_area": fields.List(fields.String, description='网络区域', default=[]),
        "resource_pool": fields.List(fields.String, description='资源池', default=[]),
        "host_machine": fields.List(fields.String, description='宿主机信息', default=[]),
        "physical_machine": fields.List(fields.String, description='物理机序列号', default=[]),
        "server_type": fields.List(fields.String, description='服务器角色', default=[]),
        "os_level": fields.List(fields.String, description='操作系统版本', default=[]),
        "middleware": fields.List(fields.String, description='中间件', default=[]),
        "status": fields.List(fields.String, description='状态', default=[]),
        "online_time": fields.List(fields.String, description='上线时间', default=[]),
        "run_status": fields.List(fields.String, description='运行状态', default=[]),
        "rec_date": fields.List(fields.String, description='录入时间', default=[]),
        "role_a": fields.List(fields.String, description='A角', default=[]),
        "role_b": fields.List(fields.String, description='B角', default=[]),
        "role_c": fields.List(fields.String, description='C角', default=[]),
    }
)

machine_cmdb_query_noip_model = ns_machine_cmdb.model(
    'machine_cmdb_query_noip',
    {
        "source": fields.List(fields.String, description='来源', default=[]),
        "machine_type": fields.List(fields.String, description='服务器类型', default=[]),
        "service_catalog": fields.List(fields.String, description='服务目录编号', default=[]),
        "app_name": fields.List(fields.String, description='系统名', default=[]),
        "project_name": fields.List(fields.String, description='系统名', default=[]),
        "app_abbre": fields.List(fields.String, description='系统简称', default=[]),
        "availability_level": fields.List(fields.String, description='可用性等级', default=[]),
        "dr_level": fields.List(fields.String, description='灾备等级', default=[]),
        # "machine_name": fields.List(fields.String, description='主机名', default=[]),
        # "ip": fields.List(fields.String, description='IP', default=[]),
        # "dmz_ip": fields.List(fields.String, description='DMZ内网IP', default=[]),
        "data_center": fields.List(fields.String, description='数据中心', default=[]),
        "network_area": fields.List(fields.String, description='网络区域', default=[]),
        "resource_pool": fields.List(fields.String, description='资源池', default=[]),
        # "host_machine": fields.List(fields.String, description='宿主机信息', default=[]),
        # "physical_machine": fields.List(fields.String, description='物理机序列号', default=[]),
        "server_type": fields.List(fields.String, description='服务器角色', default=[]),
        "os_level": fields.List(fields.String, description='操作系统版本', default=[]),
        "middleware": fields.List(fields.String, description='中间件', default=[]),
        "status": fields.List(fields.String, description='状态', default=[]),
        "online_time": fields.List(fields.String, description='上线时间', default=[]),
        "run_status": fields.List(fields.String, description='运行状态', default=[]),
        "rec_date": fields.List(fields.String, description='录入时间', default=[]),
        "role_a": fields.List(fields.String, description='A角', default=[]),
        "role_b": fields.List(fields.String, description='B角', default=[]),
        "role_c": fields.List(fields.String, description='C角', default=[]),
    }
)

# 查询栏位的每个取值的数量, 开始日期和结束日期用于给定 rec_date的取值范围，默认是查询操作系统类型
machine_cmdb_query_noip_field_model = ns_machine_cmdb.model(
    'machine_cmdb_query_noip_field',
    {
        "field_type": fields.String(description='栏位', default="os_level"),
        'start_date': fields.String(description='开始日期', default=str(datetime.now().strftime('%Y-%m-%d'))),
        'end_date': fields.String(description='结束日期', default=str(datetime.now().strftime('%Y-%m-%d'))),
    }
)

# 按照操作系统统计数量
Machine_dashboard_level_model = ns_machine_cmdb.model(
    'kms_alarm_dashboard_level',
    {
        'xAxis': fields.Nested(ns_machine_cmdb.model('xAxis', {
            "type": fields.String(description='数据类型', default='category'),
            "data": fields.List(fields.String, description='取值',
                                default=[]),
        })),
        'yAxis': fields.Nested(ns_machine_cmdb.model('yAxis', {
            "type": fields.String(description='数据类型', default='value'),
        })),
        'series': fields.Nested(ns_machine_cmdb.model('series', {
            "data": fields.List(fields.String, description='取值数量', default=[]),
            "type": fields.String(description='数据类型', default='bar'),
        })),
    }
)

# 定义外层的响应模型trend1，按告警级别分类
response_dashboard_level_model = ns_machine_cmdb.model('Response_dashboard_level', {
    'message': fields.Nested(ns_machine_cmdb.model('Message_dashboard_level', {
        "result": fields.Integer(description='查询结果返回值'),
        "result_text": fields.String(description='查询结果信息'),
        'count': fields.Integer(description='查询结果返回数据条数'),
        'data': fields.Nested(Machine_dashboard_level_model)  # data 是一个列表的列表
    }))
})


# 定义查询资源
# 查询各个栏位取值类型的统计数量
@ns_machine_cmdb.route('/postbyquerymachinecmdb')
class MachineCmdbQuery(Resource):
    @ns_machine_cmdb.expect(machine_cmdb_query_model)
    @ns_machine_cmdb.marshal_with(response_model)
    def post(self):
        try:
            # 获取传入的查询条件
            filters = []
            data = request.json

            # 检查是否所有输入的字段值都是空数组
            if all(not field_values for field_values in data.values()):
                # 如果所有字段都是空数组，则返回 202 状态码
                response_data = {
                    "message": {
                        "result": 0,  # 无效查询
                        "result_text": "请输入至少一个条件",
                        "count": 0,
                        "data": []
                    }
                }
                return response_data, 202

            # 遍历每个字段并添加到查询过滤器中
            for field_name, field_values in data.items():
                if field_values:  # 仅在有具体条件时添加到过滤器
                    filters.append(getattr(Zbfwb_machine_cmdb, field_name).in_(field_values))

            # 执行查询
            query_result = Zbfwb_machine_cmdb.query.filter(and_(*filters)).all()

            # 准备响应数据
            result_data = [machine.to_dict() for machine in query_result]

            response_data = {
                "message": {
                    "result": 1,  # 查询成功
                    "result_text": "查询成功",
                    "count": len(result_data),
                    "data": result_data
                }
            }

            # 返回封装好的响应模型，状态码200
            return response_data, 200

        except Exception as e:
            # 捕获异常并准备异常响应数据
            response_data = {
                "message": {
                    "result": 0,  # 查询异常
                    "result_text": f"查询异常: {str(e)}",
                    "count": 0,
                    "data": []
                }
            }

            # 返回封装好的异常响应模型，状态码201
            return response_data, 201


# 定义数据条数统计查询资源
# 查询返回虚机资源总数
@ns_machine_cmdb.route('/postbyquerymachinecmdbcount')
class MachineCmdbQueryCount(Resource):
    @ns_machine_cmdb.expect(machine_cmdb_query_model)
    @ns_machine_cmdb.marshal_with(response_model)
    def post(self):
        try:
            # 获取传入的查询条件
            filters = []
            data = request.json

            # 遍历每个字段并添加到查询过滤器中
            for field_name, field_values in data.items():
                if field_values:  # 仅在有具体条件时添加到过滤器
                    filters.append(getattr(Zbfwb_machine_cmdb, field_name).in_(field_values))

            # 执行统计查询：如果有条件则应用过滤器，否则统计全部条数
            if filters:
                count_result = Zbfwb_machine_cmdb.query.filter(and_(*filters)).count()
            else:
                count_result = Zbfwb_machine_cmdb.query.count()

            # 准备响应数据
            response_data = {
                "message": {
                    "result": 1,  # 查询成功
                    "result_text": "查询成功",
                    "count": count_result,  # 返回统计的条数
                    "data": []  # 因为此查询不返回数据，只返回条数
                }
            }

            # 返回封装好的响应模型，状态码200
            return response_data, 200

        except Exception as e:
            # 捕获异常并准备异常响应数据
            response_data = {
                "message": {
                    "result": 0,  # 查询异常
                    "result_text": f"查询异常: {str(e)}",
                    "count": 0,
                    "data": []
                }
            }

            # 返回封装好的异常响应模型，状态码201
            return response_data, 201


# # 定义数据条数统计查询资源
# @ns_machine_cmdb.route('/postbyquerymachinecmdbfieldscount')
# class MachineCmdbQueryFieldsCount(Resource):
#     @ns_machine_cmdb.expect(machine_cmdb_query_model)
#     @ns_machine_cmdb.marshal_with(response_count_model)
#     def post(self):
#         try:
#             # 获取传入的查询条件
#             filters = []
#             data = request.json
#
#             # 遍历每个字段并添加到查询过滤器中
#             for field_name, field_values in data.items():
#                 if field_values:  # 仅在有具体条件时添加到过滤器
#                     filters.append(getattr(Zbfwb_machine_cmdb, field_name).in_(field_values))
#
#             # 创建一个字典，用于存储每个字段的统计结果
#             statistics = {}
#
#             # 需要统计的字段列表
#             count_fields = [
#                 'source', 'machine_type', 'service_catalog', 'app_name', 'project_name',
#                 'app_abbre', 'availability_level', 'dr_level', 'machine_name', 'ip',
#                 'dmz_ip', 'data_center', 'network_area', 'resource_pool', 'host_machine',
#                 'physical_machine', 'server_type', 'os_level', 'middleware', 'status'
#             ]
#
#             # 遍历要统计的字段，分别进行统计查询
#             for field in count_fields:
#                 # 构建查询，按字段分组并统计每个字段值的出现次数
#                 query = db.session.query(
#                     getattr(Zbfwb_machine_cmdb, field).label('value'),
#                     db.func.count(getattr(Zbfwb_machine_cmdb, field)).label('count')
#                 ).filter(and_(*filters)).group_by(getattr(Zbfwb_machine_cmdb, field))
#
#                 # 执行查询并获取结果
#                 result = query.all()
#
#                 # 将查询结果添加到 statistics 字典中
#                 statistics[field] = [{'value': item.value, 'count': item.count} for item in result]
#
#             # 准备响应数据
#             response_data = {
#                 "message": {
#                     "result": 1,  # 查询成功
#                     "result_text": "查询成功",
#                     "count": sum(len(statistics[field]) for field in statistics),  # 总条数
#                     "data": [statistics]  # 将统计结果放入 data 字段
#                 }
#             }
#
#             # 返回封装好的响应模型，状态码200
#             return response_data, 200
#
#         except Exception as e:
#             # 捕获异常并准备异常响应数据
#             response_data = {
#                 "message": {
#                     "result": 0,  # 查询异常
#                     "result_text": f"查询异常: {str(e)}",
#                     "count": 0,
#                     "data": []
#                 }
#             }
#
#             # 返回封装好的异常响应模型，状态码201
#             return response_data, 201

# 定义数据条数统计查询资源
# 不包含ip地址的查询，统计各个栏位取值种类数量,合共统计
@ns_machine_cmdb.route('/postbyquerymachinecmdbfieldssts')
class MachineCmdbQueryFieldsSts(Resource):
    @ns_machine_cmdb.expect(machine_cmdb_query_noip_model)
    @ns_machine_cmdb.marshal_with(response_count_model)
    def post(self):
        try:
            # 获取传入的查询条件
            filters = []
            data = request.json

            # 遍历每个字段并添加到查询过滤器中
            for field_name, field_values in data.items():
                if field_values:  # 仅在有具体条件时添加到过滤器
                    filters.append(getattr(Zbfwb_machine_cmdb, field_name).in_(field_values))

            # 创建一个字典，用于存储每个字段的统计结果
            statistics = {}

            # 需要统计的字段列表
            count_fields = [
                'source', 'machine_type', 'service_catalog', 'app_name', 'project_name',
                'app_abbre', 'availability_level', 'dr_level',
                'data_center', 'network_area', 'resource_pool',
                'server_type', 'os_level', 'middleware', 'status'
            ]

            # 遍历要统计的字段，分别进行统计查询
            for field in count_fields:
                # 构建查询，按字段分组并统计每个字段值的出现次数
                query = db.session.query(
                    getattr(Zbfwb_machine_cmdb, field).label('value'),
                    db.func.count(getattr(Zbfwb_machine_cmdb, field)).label('count')
                ).filter(and_(*filters)).group_by(getattr(Zbfwb_machine_cmdb, field))

                # 执行查询并获取结果
                result = query.all()

                # 将查询结果添加到 statistics 字典中
                statistics[field] = [{'value': item.value, 'count': item.count} for item in result]

            # 准备响应数据，确保 data 是一个嵌套的列表结构
            response_data = {
                "message": {
                    "result": 1,  # 查询成功
                    "result_text": "查询成功",
                    "count": sum(len(statistics[field]) for field in statistics),  # 总条数
                    "data": [[{'field': field, 'results': statistics[field]}] for field in statistics]  # 调整 data 结构
                }
            }

            # 返回封装好的响应模型，状态码200
            return response_data, 200

        except Exception as e:
            # 捕获异常并准备异常响应数据
            response_data = {
                "message": {
                    "result": 0,  # 查询异常
                    "result_text": f"查询异常: {str(e)}",
                    "count": 0,
                    "data": []
                }
            }

            # 返回封装好的异常响应模型，状态码201
            return response_data, 201


# 定义数据条数统计查询资源
# 不包含ip地址的查询，统计各个栏位取值种类数量,分开统计，一个取值，一个数字
@ns_machine_cmdb.route('/postbyquerymachinecmdbfieldscount')
class MachineCmdbQueryFieldsCount(Resource):
    @ns_machine_cmdb.expect(machine_cmdb_query_noip_model)
    @ns_machine_cmdb.marshal_with(response_count_model)
    def post(self):
        try:
            # 获取传入的查询条件
            filters = []
            data = request.json

            # 遍历每个字段并添加到查询过滤器中
            for field_name, field_values in data.items():
                if field_values:  # 仅在有具体条件时添加到过滤器
                    filters.append(getattr(Zbfwb_machine_cmdb, field_name).in_(field_values))

            # 创建一个字典，用于存储每个字段的唯一值结果
            unique_values = {}

            # 需要列出唯一值的字段列表
            fields_to_query = [
                'source', 'machine_type', 'service_catalog', 'app_name', 'project_name',
                'app_abbre', 'availability_level', 'dr_level',
                'data_center', 'network_area', 'resource_pool',
                'server_type', 'os_level', 'middleware', 'status'
            ]

            # 遍历需要获取唯一值的字段
            for field in fields_to_query:
                # 构建查询，获取每个字段的唯一值
                query = db.session.query(
                    getattr(Zbfwb_machine_cmdb, field).distinct().label('value')
                ).filter(and_(*filters))

                # 执行查询并获取结果
                result = query.all()
                # print(result)
                # 将唯一值结果添加到 unique_values 字典中
                unique_values[field] = [item.value for item in result]

            # 准备响应数据，确保 data 是一个嵌套的列表结构
            response_data = {
                "message": {
                    "result": 1,  # 查询成功
                    "result_text": "查询成功",
                    "count": sum(len(unique_values[field]) for field in unique_values),  # 总条数
                    "data": [[{field: unique_values[field]}] for field in unique_values]  # 符合 fields.Raw 结构
                }
            }

            # 返回封装好的响应模型，状态码200
            return response_data, 200

        except Exception as e:
            # 捕获异常并准备异常响应数据
            response_data = {
                "message": {
                    "result": 10,  # 查询异常
                    "result_text": f"查询异常: {str(e)}",
                    "count": 0,
                    "data": []
                }
            }

            # 返回封装好的异常响应模型，状态码201
            return response_data, 201


# 按照某一栏位进行该栏位取值的统计
@ns_machine_cmdb.route('/postdashbaordmachinvaluests')
class MachineDashboardValueSts(Resource):
    @ns_machine_cmdb.expect(machine_cmdb_query_noip_field_model)
    @ns_machine_cmdb.marshal_with(response_dashboard_level_model)
    def post(self):
        try:
            # 获取传入的查询条件
            requestdata = request.json
            field_type = requestdata.get('field_type', "os_level")  # 默认是查询操作系统版本
            start_date_str = requestdata['start_date']
            end_date_str = requestdata['end_date']

            # 将日期字符串转换为 datetime 对象
            start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
            end_date = datetime.strptime(end_date_str, '%Y-%m-%d')

            # 构建查询条件，按照rec_date的时间范围进行过滤
            filters = [
                Zbfwb_machine_cmdb.rec_date >= start_date,
                Zbfwb_machine_cmdb.rec_date <= end_date
            ]

            # 查询并过滤数据
            query = Zbfwb_machine_cmdb.query.filter(and_(*filters))

            # 动态字段统计，根据传入的字段进行分组并统计每个取值的数量
            field_column = getattr(Zbfwb_machine_cmdb, field_type, None)
            if not field_column:
                raise ValueError(f"字段 {field_type} 不存在")

            # 使用group_by和count进行统计
            result = query.with_entities(field_column, db.func.count(field_column)).group_by(field_column).all()

            # 准备响应数据
            xAxis_data = [row[0] for row in result]  # 获取字段不同的取值
            series_data = [str(row[1]) for row in result]  # 获取对应取值的数量

            # 构建响应数据
            response_data = {
                "message": {
                    "result": 2,  # 查询成功
                    "result_text": "查询成功",
                    "count": len(result),
                    'data': {
                        'xAxis': {
                            "type": "category",
                            "data": xAxis_data
                        },
                        'yAxis': {
                            "type": "value"
                        },
                        'series': {
                            "data": series_data,
                            "type": "bar"
                        }
                    }
                }
            }

            # 返回封装好的响应模型，状态码200
            return response_data, 200

        except Exception as e:
            # 捕获异常并准备异常响应数据
            response_data = {
                "message": {
                    "result": 20,  # 查询异常
                    "result_text": f"查询异常: {str(e)}",
                    "count": 0,
                    "data": {
                        'xAxis': {
                            "type": "category",
                            "data": []
                        },
                        'yAxis': {
                            "type": "value"
                        },
                        'series': {
                            "data": [],
                            "type": "bar"
                        }
                    }
                }
            }

            # 返回封装好的异常响应模型，状态码201
            return response_data, 201